package com.algo.generator.sequnse;

import com.algo.generator.constant.Constants;
import com.algo.generator.mapper.IdGeneratorMapper;
import com.algo.generator.model.IdGeneratorPO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 基于 号段 + mysql 实现sequnse生成
 */
@Component("segmentLockSequnceCreator")
public class SegmentLockSequnceCreator implements SequnseCreator {

    private static final Logger logger = LoggerFactory.getLogger(SegmentLockSequnceCreator.class);

    /**
     * stub与Sequnse映射的缓存
     */
    private Map<String, SegmentSequnse> cacheSequnse = new ConcurrentHashMap<>();

    /**
     * Id_generator表mapper组件
     */
    @Autowired
    private IdGeneratorMapper idGeneratorMapper;

    /**
     * 生成 sequnse
     * @param stub 业务桩
     * @return sequnse
     */
    @Override
    public Long create(String stub) {
        SegmentSequnse segmentSequnse = initSequnseIfNecessary(stub);
        return segmentSequnse.get();
    }

    /**
     * 获取/初始化stub对应的 SegmentSequnse
     * @param stub 业务桩
     * @return 当前业务桩映射的 SegmentSequnse
     */
    private SegmentSequnse initSequnseIfNecessary (String stub) {
        SegmentSequnse segmentSequnse = cacheSequnse.get(stub);
        if (segmentSequnse == null) {
            segmentSequnse = cacheSequnse.computeIfAbsent(stub, t -> new SegmentSequnse(stub, idGeneratorMapper));
        }
        return segmentSequnse;
    }

/*    private SegmentSequnse initSequnseIfNecessary (String stub) {
        SegmentSequnse segmentSequnse = cacheSequnse.get(stub);
        if (segmentSequnse == null) {
            synchronized (cacheSequnse) {
                if ((segmentSequnse = cacheSequnse.get(stub)) == null) {
                    segmentSequnse = new SegmentSequnse(stub, idGeneratorMapper);
                    cacheSequnse.put(stub, segmentSequnse);
                }
            }
        }
        return segmentSequnse;
    }*/

    /**
     * 分段锁Sequnse
     */
    private static class SegmentSequnse {

        private IdGeneratorMapper idGeneratorMapper;

        private volatile boolean isUpdating = false;

        protected String stub;

        protected IdGeneratorPO insertPO;

        protected volatile long seq;
        protected volatile long maxSeq;

        public SegmentSequnse(String stub, IdGeneratorMapper idGeneratorMapper) {
            this.insertPO = new IdGeneratorPO(stub);
            this.idGeneratorMapper = idGeneratorMapper;
            init();
        }

        /**
         * 初始化方法
         */
        private void init() {
            Long initValue = idGeneratorMapper.getIdByStub(stub);

            // 如果数据库中不存在的话，先插入一条，获取开始的 seq
            if (initValue == null) {
                idGeneratorMapper.insert(insertPO);
                seq = insertPO.getId() - 1;
            } else {
                seq = initValue;
            }

            // 插入一条数据，获取seq的最大值
            idGeneratorMapper.insert(insertPO);
            maxSeq = insertPO.getId();
        }

        /**
         * 获取序列号
         * @return
         */
        public Long get() {
            // 这边可能会有多个线程同时过来获取锁，如果出现需要扩容的场景时，只有一个线程会去执行同步更新的操作
            // 其余的线程会在这边等待获取锁，如果拿到锁的线程会休眠 20ms后再过来继续尝试获取，这边并没有采用通知的方式
            synchronized (this) {
                if (isUpdating) {
                    return sleepAndTryAgain(Constants.SEQ_FLUSHING_GET_INTERVAL);
                }
                // 如果小于最大值，直接返回
                if (++ seq < maxSeq) {
                    return seq;
                }
                // 需要此线程去执行 segment更新操作
                isUpdating = true;
            }

            if (logger.isDebugEnabled()) {
                logger.debug("sequnse is updating, The thread executing is {}, current seq {}, maxSeq {}",
                        Thread.currentThread().getName(), seq, maxSeq);
            }

            // 这边只有一个线程会执行同步更新的代码
            return syncUpdateSegment();
        }

        /**
         * 同步更新号段
         */
        private Long syncUpdateSegment() {
            // 更新并重新赋值操作
            idGeneratorMapper.insert(insertPO);
            seq = maxSeq;
            maxSeq = insertPO.getId();
            isUpdating = false;
            return seq;
        }

        /**
         * 休眠指定时间后再次尝试获取
         * @param sleepTime
         * @return
         */
        private Long sleepAndTryAgain(Integer sleepTime) {
            try {
                Thread.currentThread().sleep(sleepTime);
            } catch (Exception e) {}
            // 重新获取
            return get();
        }
    }
}
